/*
 *    Copyright 2019 The aio-socket Project
 *
 *    The aio-socket Project Licenses this file to you under the Apache License,
 *    Version 2.0 (the "License"); you may not use this file except in compliance
 *    with the License. You may obtain a copy of the License at:
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package io.task.socket.plugins;

import io.task.socket.Monitor;
import io.task.socket.Packet;
import io.task.socket.core.AbstractChannelContext;
import io.task.socket.enums.ProtocolEnum;
import io.task.socket.enums.StateMachineEnum;
import io.task.socket.exception.AioDecoderException;
import io.task.socket.exception.AioEncoderException;
import io.task.socket.intf.AioHandler;
import io.task.socket.intf.Handler;
import io.task.socket.utils.pool.memory.MemoryUnit;

import java.nio.channels.AsynchronousSocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * aio-socket 插件嵌入类
 *
 * @author MDong
 * @version 2.10.1.v20211002-RELEASE
 */
public class Plugins implements Handler, Monitor {

	/**
	 * 服务器第一个协议解析器
	 */
	private ProtocolEnum serverDefaultFirstProtocol = null;

	/**
	 * 插件项
	 */
	private final List<Plugin> plugins = new ArrayList<>();

	/**
	 * 处理器集合
	 */
	private final Map<ProtocolEnum, AioHandler> handlers = new HashMap<>();

	@Override
	public AsynchronousSocketChannel shouldAccept(AsynchronousSocketChannel channel) {
		AsynchronousSocketChannel acceptChannel = channel;
		for (Plugin plugin : plugins) {
			acceptChannel = plugin.shouldAccept(acceptChannel);
			if (acceptChannel == null) {
				return null;
			}
		}
		return acceptChannel;
	}

	@Override
	public void afterRead(AbstractChannelContext context, int readSize) {
		for (Plugin plugin : plugins) {
			plugin.afterRead(context, readSize);
		}
	}

	@Override
	public void beforeRead(AbstractChannelContext context) {
		for (Plugin plugin : plugins) {
			plugin.beforeRead(context);
		}
	}

	@Override
	public void afterWrite(AbstractChannelContext context, int writeSize) {
		for (Plugin plugin : plugins) {
			plugin.afterWrite(context, writeSize);
		}
	}

	@Override
	public void beforeWrite(AbstractChannelContext context) {
		for (Plugin plugin : plugins) {
			plugin.beforeWrite(context);
		}
	}

	@Override
	public Packet handle(AbstractChannelContext abstractChannelContext, Packet packet) {
		boolean flag = true;
		for (Plugin plugin : plugins) {
			if (!plugin.beforeProcess(abstractChannelContext, packet)) {
				flag = false;
			}
		}
		return flag ? handlers.get(abstractChannelContext.getProtocol()).handle(abstractChannelContext, packet) : null;
	}

	@Override
	public Packet decode(MemoryUnit readBuffer, AbstractChannelContext abstractChannelContext) throws AioDecoderException {
		Packet packet = null;
		ProtocolEnum protocol = abstractChannelContext.getProtocol();
		if (protocol != null) {
			packet = handlers.get(protocol).decode(readBuffer, abstractChannelContext);
		} else {
			for (AioHandler handler : handlers.values()) {
				packet = handler.decode(readBuffer, abstractChannelContext);
				if (packet != null) {
					// 解码成功，设置协议。中断链式解码
					abstractChannelContext.setProtocol(handler.name());
					break;
				}
			}
		}
		if (packet != null) {
			for (Plugin plugin : plugins) {
				plugin.afterDecode(packet, abstractChannelContext);
			}
		}
		return packet;
	}

	@Override
	public void encode(Packet packet, AbstractChannelContext abstractChannelContext) throws AioEncoderException {
		for (Plugin plugin : plugins) {
			plugin.beforeEncode(packet, abstractChannelContext);
		}
		handlers.get(abstractChannelContext.getProtocol()).encode(packet, abstractChannelContext);
	}

	@Override
	public void stateEvent(AbstractChannelContext abstractChannelContext, StateMachineEnum stateMachineEnum, Throwable throwable) {
		for (Plugin plugin : plugins) {
			plugin.stateEvent(stateMachineEnum, abstractChannelContext, throwable);
		}
		if (abstractChannelContext.getProtocol() != null) {
			handlers.get(abstractChannelContext.getProtocol()).stateEvent(abstractChannelContext, stateMachineEnum, throwable);
			return;
		}
		// 当前通道未确定协议就触发状态机，则使用第一个处理器进行处理
		handlers.get(serverDefaultFirstProtocol).stateEvent(abstractChannelContext, stateMachineEnum, throwable);
	}

	public void addAioHandler(AioHandler handler) {
		handlers.put(handler.name(), handler);
		if (serverDefaultFirstProtocol == null) {
			serverDefaultFirstProtocol = handler.name();
		}
	}

	public final Plugins addPlugin(Plugin plugin) {
		this.plugins.add(plugin);
		return this;
	}
}
